 /*******************************************************************************
  * Copyright (c) 2005, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  * Brad Reynolds - bug 159539
  * Brad Reynolds - bug 140644
  * Brad Reynolds - bug 159940
  * Brad Reynolds - bug 116920, 159768
  *******************************************************************************/
 package org.eclipse.core.databinding;

 import java.util.Iterator ;

 import org.eclipse.core.databinding.observable.Observables;
 import org.eclipse.core.databinding.observable.Realm;
 import org.eclipse.core.databinding.observable.list.IObservableList;
 import org.eclipse.core.databinding.observable.list.WritableList;
 import org.eclipse.core.databinding.observable.map.IObservableMap;
 import org.eclipse.core.databinding.observable.value.IObservableValue;
 import org.eclipse.core.internal.databinding.ValidationStatusMap;
 import org.eclipse.core.runtime.Assert;
 import org.eclipse.core.runtime.IStatus;

 /**
  * A DataBindingContext is the point of contact for the creation and management of
  * {@link Binding bindings}.
  * <p>
  * A DataBindingContext provides the following abilities:
  * <ul>
  * <li>Ability to create bindings between
  * {@link IObservableValue observable values}.</li>
  * <li>Ability to create bindings between
  * {@link IObservableList observable lists}.</li>
  * <li>Access to the bindings created by the instance.</li>
  * <li>Access to the validation status of its bindings.</li>
  * </ul>
  * </p>
  * <p>
  * Multiple contexts can be used at any point in time. One strategy for the
  * management of contexts is the aggregation of validation statuses. For example
  * an <code>IWizardPage</code> could use a single context and the statuses
  * could be aggregated to set the page status and fulfillment. Each page in the
  * <code>IWizard</code> would have its own context instance.
  * </p>
  *
  * @since 1.0
  */
 public class DataBindingContext {
     private WritableList bindings;

     /**
      * Unmodifiable version of {@link #bindings} for public exposure.
      */
     private IObservableList unmodifiableBindings;

     private IObservableMap validationStatusMap;

     private Realm validationRealm;

     /**
      * Creates a data binding context, using the current default realm for the
      * validation observables.
      *
      * @see Realm
      */
     public DataBindingContext() {
         this(Realm.getDefault());
     }

     /**
      * Creates a data binding context using the given realm for the validation
      * observables.
      *
      * @param validationRealm
      * the realm to be used for the validation observables
      *
      * @see Realm
      */
     public DataBindingContext(Realm validationRealm) {
         Assert.isNotNull(validationRealm);
         this.validationRealm = validationRealm;
         bindings = new WritableList(validationRealm);

         unmodifiableBindings = Observables.unmodifiableObservableList(bindings);
         validationStatusMap = new ValidationStatusMap(validationRealm,
                 bindings);
     }

     /**
      * Creates a {@link Binding} to synchronize the values of two
      * {@link IObservableValue observable values}. During synchronization
      * validation and conversion can be employed to customize the process. For
      * specifics on the customization of the process see
      * {@link UpdateValueStrategy}.
      *
      * @param targetObservableValue
      * target value, commonly a UI widget
      * @param modelObservableValue
      * model value
      * @param targetToModel
      * strategy to employ when the target is the source of the change
      * and the model is the destination
      * @param modelToTarget
      * strategy to employ when the model is the source of the change
      * and the target is the destination
      * @return created binding
      *
      * @see UpdateValueStrategy
      */
     public final Binding bindValue(IObservableValue targetObservableValue,
             IObservableValue modelObservableValue,
             UpdateValueStrategy targetToModel, UpdateValueStrategy modelToTarget) {
         UpdateValueStrategy targetToModelStrategy = targetToModel != null ? targetToModel
                         : createTargetToModelUpdateValueStrategy(targetObservableValue, modelObservableValue);
         UpdateValueStrategy modelToTargetStrategy = modelToTarget != null ? modelToTarget
                 : createModelToTargetUpdateValueStrategy(modelObservableValue, targetObservableValue);
         targetToModelStrategy.fillDefaults(targetObservableValue, modelObservableValue);
         modelToTargetStrategy.fillDefaults(modelObservableValue, targetObservableValue);
         ValueBinding result = new ValueBinding(targetObservableValue,
                 modelObservableValue, targetToModelStrategy,
                 modelToTargetStrategy);
         result.init(this);
         return result;
     }

     /**
      * Returns an update value strategy to be used for copying values from the
      * from value to the to value. Clients may override.
      *
      * @param fromValue
      * @param toValue
      * @return a update value strategy
      */
     protected UpdateValueStrategy createModelToTargetUpdateValueStrategy(
             IObservableValue fromValue, IObservableValue toValue) {
         return new UpdateValueStrategy();
     }

     /**
      * Returns an update value strategy to be used for copying values from the
      * from value to the to value. Clients may override.
      *
      * @param fromValue
      * @param toValue
      * @return a update value strategy
      */
     protected UpdateValueStrategy createTargetToModelUpdateValueStrategy(
             IObservableValue fromValue, IObservableValue toValue) {
         return new UpdateValueStrategy();
     }
     
     /**
      * Creates a {@link Binding} to synchronize the values of two
      * {@link IObservableList observable lists}. During synchronization
      * validation and conversion can be employed to customize the process. For
      * specifics on the customization of the process see
      * {@link UpdateListStrategy}.
      *
      * @param targetObservableList
      * target list, commonly a list representing a list in the UI
      * @param modelObservableList
      * model list
      * @param targetToModel
      * strategy to employ when the target is the source of the change
      * and the model is the destination
      * @param modelToTarget
      * strategy to employ when the model is the source of the change
      * and the target is the destination
      * @return created binding
      *
      * @see UpdateListStrategy
      */
     public final Binding bindList(IObservableList targetObservableList,
             IObservableList modelObservableList,
             UpdateListStrategy targetToModel, UpdateListStrategy modelToTarget) {
         UpdateListStrategy targetToModelStrategy = targetToModel != null ? targetToModel
                 : createTargetToModelUpdateListStrategy(targetObservableList,
                         modelObservableList);
         UpdateListStrategy modelToTargetStrategy = modelToTarget != null ? modelToTarget
                 : createModelToTargetUpdateListStrategy(modelObservableList,
                         targetObservableList);
         targetToModelStrategy.fillDefaults(targetObservableList,
                 modelObservableList);
         modelToTargetStrategy.fillDefaults(modelObservableList,
                 targetObservableList);
         ListBinding result = new ListBinding(targetObservableList,
                 modelObservableList, targetToModelStrategy,
                 modelToTargetStrategy);
         result.init(this);
         return result;
     }

     /**
      * @param modelObservableList
      * @param targetObservableList
      * @return an update list strategy
      */
     protected UpdateListStrategy createModelToTargetUpdateListStrategy(
             IObservableList modelObservableList,
             IObservableList targetObservableList) {
         return new UpdateListStrategy();
     }

     /**
      * @param targetObservableList
      * @param modelObservableList
      * @return an update list strategy
      */
     protected UpdateListStrategy createTargetToModelUpdateListStrategy(
             IObservableList targetObservableList,
             IObservableList modelObservableList) {
         return new UpdateListStrategy();
     }

     /**
      * Disposes of this data binding context and all bindings that were added to
      * this context.
      */
     public final void dispose() {
         Binding[] bindingArray = (Binding[]) bindings.toArray(new Binding[bindings.size()]);
         for (int i = 0; i < bindingArray.length; i++) {
             bindingArray[i].dispose();
         }
     }

     /**
      * Returns an unmodifiable observable list with elements of type
      * {@link Binding}, ordered by time of addition.
      *
      * @return the observable list containing all bindings
      */
     public final IObservableList getBindings() {
         return unmodifiableBindings;
     }

     /**
      * Returns an observable map from bindings (type: {@link Binding}) to
      * statuses (type: {@link IStatus}). The keys of the map are the bindings
      * returned by {@link #getBindings()}, and the values are the current
      * validaion status objects for each binding.
      *
      * @return the observable map from bindings to status objects.
      */
     public final IObservableMap getValidationStatusMap() {
         return validationStatusMap;
     }

     /**
      * Adds the given binding to this data binding context.
      *
      * @param binding
      * The binding to add.
      */
     public void addBinding(Binding binding) {
         bindings.add(binding);
     }

     /**
      * Updates all model observable objects to reflect the current state of the
      * target observable objects.
      *
      */
     public final void updateModels() {
         for (Iterator it = bindings.iterator(); it.hasNext();) {
             Binding binding = (Binding) it.next();
             binding.updateTargetToModel();
         }
     }

     /**
      * Updates all target observable objects to reflect the current state of the
      * model observable objects.
      *
      */
     public final void updateTargets() {
         for (Iterator it = bindings.iterator(); it.hasNext();) {
             Binding binding = (Binding) it.next();
             binding.updateModelToTarget();
         }
     }

     /**
      * Removes the given binding.
      *
      * @param binding
      * @return <code>true</code> if was associated with the context,
      * <code>false</code> if not
      */
     public boolean removeBinding(Binding binding) {
         return bindings.remove(binding);
     }

     /**
      * Returns the validation realm.
      *
      * @return the realm for the validation observables
      * @see Realm
      */
     public final Realm getValidationRealm() {
         return validationRealm;
     }
 }

